package org.fisco.bcos.sdk.v3.client.sign;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * @author 曳戈泰尔
 * @version 1.0
 * @description MD5 Helper
 *     <p>生成有序map，签名，验签
 * @date 2023/5/17 11:53 AM
 */
public class Md5Helper {

    // 日志
    private static final Logger log = LoggerFactory.getLogger(Md5Helper.class);

    /**
     * 生成sign
     *
     * <p>有序map
     *
     * @param params 请求参数 有序map
     * @param key 密钥
     * @return 签名
     */
    public static String createSign(SortedMap<String, String> params, String key) {

        // 生成待签名的字符串
        StringBuilder sb = new StringBuilder();
        Set<Map.Entry<String, String>> es = params.entrySet();
        for (Map.Entry<String, String> entry : es) {
            String k = entry.getKey();
            String v = entry.getValue();
            // 去除空值，sign，key
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"appSecret".equals(k)) {
                sb.append(k).append("=").append(v).append("&");
            }
        }
        // 添加key
        sb.append("appSecret=").append(key);

        // 待签名的字符串
        String waitSign = sb.toString();
        // 判断待签名参数的合法性
        if (StringUtils.isBlank(waitSign)) {
            log.error("待签名参数为空");

            return null;
        }

        // 生成签名
        String sign = md5(waitSign);
        if (StringUtils.isBlank(sign)) {
            log.error("签名为空");

            return null;
        }

        // 返回
        return sign;
    }

    /**
     * 生成sign
     *
     * <p>一般map
     *
     * @param params 请求参数 一般map
     * @param key 密钥
     * @return 签名
     */
    public static String createSign(Map<String, String> params, String key) {

        // 生成有序map
        SortedMap<String, String> sortedMap = getSortedMap(params);

        // 生成待签名的字符串
        StringBuilder sb = new StringBuilder();
        Set<Map.Entry<String, String>> es = sortedMap.entrySet();
        for (Map.Entry<String, String> entry : es) {
            String k = entry.getKey();
            String v = entry.getValue();
            // 去除空值，sign，key
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"appSecret".equals(k)) {
                sb.append(k).append("=").append(v).append("&");
            }
        }
        // 添加key
        sb.append("appSecret=").append(key);

        // 待签名的字符串
        String waitSign = sb.toString();
        // 判断待签名参数的合法性
        if (StringUtils.isBlank(waitSign)) {
            log.error("待签名参数为空");

            return null;
        }

        // 生成签名
        String sign = md5(waitSign);
        if (StringUtils.isBlank(sign)) {
            log.error("签名为空");

            return null;
        }

        // 返回
        return sign;
    }

    /**
     * 校验签名
     *
     * <p>有序map
     *
     * @param params 请求参数 有序map
     * @param key 密钥
     * @return 是否正确
     */
    public static boolean isCorrectSign(SortedMap<String, String> params, String key) {

        // 生成签名
        String sign = createSign(params, key);

        // 获取参数中的签名
        String paramSign = params.get("sign");

        // 判断签名是否正确
        return paramSign.equals(sign);
    }

    /**
     * 校验签名
     *
     * <p>一般map
     *
     * @param params 请求参数 一般map
     * @param key 密钥
     * @return 是否正确
     */
    public static boolean isCorrectSign(Map<String, String> params, String key) {

        // 生成签名
        String sign = createSign(params, key);

        // 获取参数中的签名
        String paramSign = params.get("sign");

        // 判断签名是否正确
        return paramSign.equals(sign);
    }

    /**
     * 获取有序map
     *
     * @param map 请求参数
     * @return 有序map
     */
    public static SortedMap<String, String> getSortedMap(Map<String, String> map) {

        // 生成有序map
        SortedMap<String, String> sortedMap = new TreeMap<>();
        for (Map.Entry<String, String> entry : map.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            String temp = "";
            if (null != value) {
                temp = value.trim();
            }
            sortedMap.put(key, temp);
        }

        return sortedMap;
    }

    /**
     * md5常用工具类
     *
     * @param data 待加密字符串
     * @return 加密后字符串
     */
    public static String md5(String data) {

        try {
            // 获取MD5实例
            MessageDigest md5 = MessageDigest.getInstance("MD5");

            // 加密
            byte[] array = md5.digest(data.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte item : array) {
                sb.append(Integer.toHexString((item & 0xFF) | 0x100), 1, 3);
            }

            // 返回加密后字符串，转换成大写
            return sb.toString().toUpperCase();

        } catch (Exception e) {
            log.error("MD5加密失败，data={}，原因={}", data, e.getMessage());

            return null;
        }
    }
}
